#include "Managers/BlocksManager.h"

#include "Blocks/AllBlockInc.h"

#include "Terrain/Terrain.h"
#include "Utils/Utils.h"
#include "Log.h"

namespace PocketSurvival
{
    static bool skipConflict = true;
    static bool haveError = false;

    BlocksManager::BlocksManager()
    {
        std::string blocksDataText;
        
        if(Utils::LoadTextFile("./content/BlocksData.txt", blocksDataText) == true)
        {
            loadBlockSource(blocksDataText);
        }
        else
        {
            throw std::runtime_error("无法读取到./content/BlocksData.txt的数据");
        }
    }
    
    BlocksManager::~BlocksManager()
    {
        for(int32_t index = 0; index < 1024; index++)
        {
            if(blocks[index] != nullptr)
            {
                delete blocks[index];
            }
        }
    }

    void BlocksManager::loadGame()
    {
        registerBlock<AirBlock>();
        registerBlock<BedrockBlock>();
        registerBlock<DirtBlock>();
        registerBlock<GraniteBlock>();
        registerBlock<SandstoneBlock>();
        registerBlock<CobblestoneBlock>();
        registerBlock<GravelBlock>();
        registerBlock<SandBlock>();
        registerBlock<GrassBlock>();
        registerBlock<OakWoodBlock>();
        registerBlock<BirchWoodBlock>();
        registerBlock<SpruceWoodBlock>();
        registerBlock<OakLeavesBlock>();
        registerBlock<BirchLeavesBlock>();
        registerBlock<SpruceLeavesBlock>();

        registerBlock<GlassBlock>();

        registerBlock<CoalOreBlock>();
        registerBlock<WickerLampBlock>();
        registerBlock<WaterBlock>();
        registerBlock<TallGrassBlock>();
        registerBlock<RedFlowerBlock>();
        registerBlock<PlanksBlock>();
        registerBlock<CoalChunkBlock>();
        registerBlock<StickBlock>();
        registerBlock<PurpleFlowerBlock>();
        registerBlock<WhiteFlowerBlock>();
        registerBlock<StoneBrickBlock>();
        registerBlock<CraftingTableBlock>();
        registerBlock<DryBushBlock>();
        registerBlock<StoneAxeBlock>();
        
        registerBlock<TorchBlock>();

        registerBlock<IronShovelBlock>();
        registerBlock<IronAxeBlock>();
        registerBlock<IronPickaxeBlock>();
        registerBlock<IronOreBlock>();
        registerBlock<IronIngotBlock>();
        registerBlock<CopperOreBlock>();
        registerBlock<CopperIngotBlock>();
        registerBlock<MalachiteChunkBlock>();
        registerBlock<FramedGlassBlock>();
        registerBlock<ChestBlock>();
        registerBlock<IronBlock>();
        registerBlock<CopperBlock>();
        registerBlock<CobblestoneStairsBlock>();
        registerBlock<WoodenStairsBlock>();
        registerBlock<StoneBrickStairsBlock>();
        registerBlock<SandstoneStairsBlock>();
        registerBlock<SandstoneSlabBlock>();
        registerBlock<CobblestoneSlabBlock>();
        registerBlock<StoneBrickSlabBlock>();
        registerBlock<WoodenSlabBlock>();
        registerBlock<WoodenDoorBlock>();
        registerBlock<IronDoorBlock>();
        registerBlock<CellDoorBlock>();
        registerBlock<WoodenLadderBlock>();
        registerBlock<WindowBlock>();
        registerBlock<SnowBlock>();
        registerBlock<IceBlock>();
        registerBlock<ChristmasTreeBlock>();
        registerBlock<FurnaceBlock>();
        registerBlock<LitFurnaceBlock>();
        registerBlock<LimestoneBlock>();
        registerBlock<BasaltBlock>();
        registerBlock<MarbleBlock>();
        registerBlock<MarbleStairsBlock>();
        registerBlock<MarbleSlabBlock>();
        registerBlock<MalachiteBlock>();


        registerBlock<ClayBlock>();
        registerBlock<BricksBlock>();
        registerBlock<BrickBlock>();
        registerBlock<BrickSlabBlock>();
        registerBlock<BrickStairsBlock>();
        registerBlock<RawBirdBlock>();
        registerBlock<CookedBirdBlock>();
        registerBlock<StoneChunkBlock>();
        registerBlock<WoodenSpearBlock>();
        registerBlock<StoneSpearBlock>();
        registerBlock<IronSpearBlock>();
        registerBlock<WoodenTrapdoorBlock>();
        registerBlock<CellTrapdoorBlock>();
        registerBlock<SnowballBlock>();
        registerBlock<SpikedPlankBlock>();
        registerBlock<GrassTrapBlock>();
        registerBlock<RawMeatBlock>();
        registerBlock<CookedMeatBlock>();
        registerBlock<EmptyBucketBlock>();
        registerBlock<WaterBucketBlock>();
        registerBlock<MagmaBlock>();
        registerBlock<MagmaBucketBlock>();
        registerBlock<WoodenFenceBlock>();
        registerBlock<BasaltSlabBlock>();
        registerBlock<BasaltStairsBlock>();
        registerBlock<WoodenPostedSignBlock>();
        registerBlock<WoodenAttachedSignBlock>();
        registerBlock<LargeDryBushBlock>();
        registerBlock<SaltpeterOreBlock>();
        registerBlock<SulphurOreBlock>();
        registerBlock<SaltpeterChunkBlock>();
        registerBlock<SulphurChunkBlock>();
        registerBlock<FireBlock>();
        registerBlock<SmallGunpowderKegBlock>();
        registerBlock<MediumGunpowderKegBlock>();
        registerBlock<LargeGunpowderKegBlock>();
        registerBlock<MatchBlock>();
        registerBlock<GunpowderBlock>();
        registerBlock<MilkBucketBlock>();
        registerBlock<DiamondChunkBlock>();
        registerBlock<DiamondOreBlock>();
        registerBlock<DiamondShovelBlock>();
        registerBlock<DiamondAxeBlock>();
        registerBlock<DiamondPickaxeBlock>();
        registerBlock<DiamondSpearBlock>();
        registerBlock<CompassBlock>();
        registerBlock<EggBlock>();
        registerBlock<SaplingBlock>();
        registerBlock<ThermometerBlock>();
        registerBlock<HygrometerBlock>();
        registerBlock<WoodenClubBlock>();
        registerBlock<StoneClubBlock>();
        registerBlock<IronMacheteBlock>();
        registerBlock<DiamondMacheteBlock>();
        registerBlock<DiamondBlock>();
        registerBlock<CactusBlock>();
        registerBlock<PaintStripperBucketBlock>();
        registerBlock<PaintBucketBlock>();
        registerBlock<PigmentBlock>();
        registerBlock<PumpkinBlock>();
        registerBlock<JackOLanternBlock>();
        registerBlock<WireBlock>();
        registerBlock<NandGateBlock>();
        registerBlock<NorGateBlock>();
        registerBlock<StoneSlabBlock>();
        registerBlock<AndGateBlock>();
        registerBlock<BatteryBlock>();
        registerBlock<LightbulbBlock>();
        registerBlock<NotGateBlock>();
        registerBlock<SwitchBlock>();
        registerBlock<ButtonBlock>();
        registerBlock<OrGateBlock>();
        registerBlock<PressurePlateBlock>();
        registerBlock<DelayGateBlock>();
        registerBlock<SRLatchBlock>();
        registerBlock<DetonatorBlock>();
        registerBlock<GermaniumOreBlock>();
        registerBlock<GermaniumChunkBlock>();
        registerBlock<CoalBlock>();
        registerBlock<PhotodiodeBlock>();
        registerBlock<LedBlock>();
        registerBlock<WireThroughPlanksBlock>();
        registerBlock<WireThroughStoneBlock>();
        registerBlock<WireThroughSemiconductorBlock>();
        registerBlock<XorGateBlock>();
        registerBlock<RandomGeneratorBlock>();
        registerBlock<SaddleBlock>();
        registerBlock<LeatherBlock>();
        registerBlock<WhistleBlock>();
        registerBlock<RawFishBlock>();
        registerBlock<CookedFishBlock>();
        registerBlock<BasaltFenceBlock>();
        registerBlock<BrickFenceBlock>();
        registerBlock<CopperAxeBlock>();
        registerBlock<WoodenFenceGateBlock>();
        registerBlock<MagnetBlock>();
        registerBlock<SoilBlock>();
        registerBlock<WoodenRakeBlock>();

        registerBlock<IronRakeBlock>();
        registerBlock<DiamondRakeBlock>();
        registerBlock<SeedsBlock>();
        registerBlock<RyeBlock>();
        registerBlock<FlourBlock>();
        registerBlock<DoughBlock>();
        registerBlock<BreadBlock>();
        registerBlock<BoatBlock>();
        registerBlock<MotionDetectorBlock>();
        registerBlock<DigitalToAnalogConverterBlock>();
        registerBlock<AnalogToDigitalConverterBlock>();
        registerBlock<FourLedBlock>();
        registerBlock<SoundGeneratorBlock>();
        registerBlock<CounterBlock>();
        registerBlock<SevenSegmentDisplayBlock>();
        registerBlock<MemoryBankBlock>();
        registerBlock<RealTimeClockBlock>();
        registerBlock<TruthTableCircuitBlock>();
        registerBlock<GravestoneBlock>();
        registerBlock<TreasureGeneratorBlock>();
        registerBlock<BowBlock>();
        registerBlock<ArrowBlock>();
        registerBlock<IronFenceBlock>();
        registerBlock<IronFenceGateBlock>();
        registerBlock<RodBlock>();
        registerBlock<FeatherBlock>();
        registerBlock<IvyBlock>();
        registerBlock<StringBlock>();
        registerBlock<TargetBlock>();
        registerBlock<CrossbowBlock>();
        registerBlock<BombBlock>();
        registerBlock<StoneFenceBlock>();
        registerBlock<ClothingBlock>();
        registerBlock<CottonBlock>();
        registerBlock<CottonWadBlock>();
        registerBlock<CanvasBlock>();
        registerBlock<FurBlock>();
        registerBlock<CarpetBlock>();
        registerBlock<CampfireBlock>();
        registerBlock<IronPostedSignBlock>();
        registerBlock<IronAttachedSignBlock>();
        registerBlock<MusketBlock>();
        registerBlock<IronLadderBlock>();
        registerBlock<BulletBlock>();
        registerBlock<FireworksBlock>();
        registerBlock<DispenserBlock>();
        registerBlock<StoneStairsBlock>();
        registerBlock<CopperPickaxeBlock>();
        registerBlock<CopperRakeBlock>();
        registerBlock<CopperSpearBlock>();
        registerBlock<CopperMacheteBlock>();
        registerBlock<CopperShovelBlock>();
        registerBlock<WireThroughBricksBlock>();
        registerBlock<AdjustableDelayGateBlock>();
        registerBlock<TallSpruceLeavesBlock>();
        registerBlock<SeaUrchinBlock>();
        registerBlock<FurnitureBlock>();
        registerBlock<IncendiaryBombBlock>();
        registerBlock<StarfishBlock>();
        registerBlock<IronHammerBlock>();
        registerBlock<SemiconductorBlock>();
        registerBlock<KelpBlock>();
        registerBlock<SeagrassBlock>();
        registerBlock<SmallIncendiaryKegBlock>();
        registerBlock<MediumIncendiaryKegBlock>();
        registerBlock<LargeIncendiaryKegBlock>();
        registerBlock<PistonBlock>();
        registerBlock<PistonHeadBlock>();
        registerBlock<RottenBirdBlock>();
        registerBlock<RottenMeatBlock>();
        registerBlock<RottenFishBlock>();
        registerBlock<RottenBreadBlock>();
        registerBlock<WireThroughCobblestoneBlock>();
        registerBlock<RottenPumpkinBlock>();
        registerBlock<RottenMilkBucketBlock>();
        registerBlock<RottenEggBlock>();
        registerBlock<RottenDoughBlock>();
        registerBlock<ExperienceBlock>();
        registerBlock<IronOreChunkBlock>();
        registerBlock<GermaniumOreChunkBlock>();
        registerBlock<PumpkinSoupBucketBlock>();
        registerBlock<RottenPumpkinSoupBucketBlock>();
        registerBlock<OneLedBlock>();
        registerBlock<MulticoloredLedBlock>();
        registerBlock<MimosaWoodBlock>();
        registerBlock<MimosaLeavesBlock>();
        registerBlock<ShadowBlock>();
        registerBlock<CairnBlock>();

        skipConflict = false;
        if(haveError)
        {
           throw std::logic_error("存在冲突的方块id"); 
        }


        for(Block* block : blocks)
        {
            if(block != nullptr)
            {
                block->initialize();
            }
        }
    }

    /**
     * @brief 加载方块
     * @note 会通过方块的名称来查表找到对应的数据，然后再进行加载，若方块已经存在方块表中或者不存在对应的源数据，则会报错
    */
    void BlocksManager::loadBlock(Block *block)
    {
        if(block == nullptr)
        {
            return;
        }
        // 要先确定方块不再方块表中
        if(blocks[block->getIndex()] != nullptr)
        {
            if(skipConflict)
            {
                Log::Warn(fmt::format("方块ID: {} 存在冲突，冲突得方块名称: {}", block->getIndex(), block->getBlockName()));
            }
            else
            {
                throw std::logic_error(fmt::format("方块ID: {} 存在冲突，冲突得方块名称: {}", block->getIndex(), block->getBlockName()));
            }
        }
        // 不在方块表中，那么该方块进写进表中
        blocks[block->getIndex()] = block;

        // 按方块名称查找源数据内容，并加载
        auto it = m_blockDataMaps.find(block->getBlockName());
        if(it != m_blockDataMaps.end())
        {
            std::vector<std::string> &blockData = it->second;

            block->DefaultDisplayName           = blockData[1];
            block->DefaultCategory              = blockData[2];
            block->Behaviors                    = blockData[3];
            block->DisplayOrder                 = atoi(blockData[4].c_str());
            block->DefaultIconBlockOffset       = blockData[5];
            block->DefaultIconViewOffset        = blockData[6];
            block->DefaultIconViewScale         = blockData[7];
            block->FirstPersonScale             = blockData[8];
            block->FirstPersonOffset            = blockData[9];
            block->FirstPersonRotation          = blockData[10];
            block->InHandScale                  = blockData[11];
            block->InHandOffset                 = blockData[12];
            block->InHandRotation               = blockData[13];
            block->CraftingId                   = blockData[14];
            block->DefaultCreativeData          = (blockData[15] == "" ? -1 : std::stoi(blockData[15]));
            block->IsCollidable                 = (blockData[16] == "TRUE" ? true : false);
            block->IsPlaceable                  = (blockData[17] == "TRUE" ? true : false);
            block->IsDiggingTransparent         = (blockData[18] == "TRUE" ? true : false);
            block->IsPlacementTransparent       = (blockData[19] == "TRUE" ? true : false);
            block->DefaultIsInteractive         = (blockData[20] == "TRUE" ? true : false);
            block->IsEditable                   = (blockData[21] == "TRUE" ? true : false);
            block->IsNonDuplicable              = (blockData[22] == "TRUE" ? true : false);
            block->IsGatherable                 = (blockData[23] == "TRUE" ? true : false);
            block->HasCollisionBehavior         = (blockData[24] == "TRUE" ? true : false);
            block->KillsWhenStuck               = (blockData[25] == "TRUE" ? true : false);
            block->IsFluidBlocker               = (blockData[26] == "TRUE" ? true : false);
            block->IsTransparent                = (blockData[27] == "TRUE" ? true : false);
            block->DefaultShadowStrength        = (blockData[28] == "" ? 0 : std::stoi(blockData[28]));
            block->LightAttenuation             = (blockData[29] == "" ? 0 : std::stoi(blockData[29]));
            block->DefaultEmittedLightAmount    = (blockData[30] == "" ? 0 : std::stoi(blockData[30]));
            block->ObjectShadowStrength         = (blockData[31] == "" ? 0 : std::stof(blockData[31]));
            block->DefaultDropContent           = (blockData[32] == "#" ? block->getIndex() : atoi(blockData[32].c_str()));
            block->DefaultDropCount             = (blockData[33] == "" ? 1.0f : std::stof(blockData[33]));
            block->DefaultExperienceCount       = (blockData[34] == "" ? 0 : std::stof(blockData[34]));
            block->RequiredToolLevel            = (blockData[35] == "" ? 0 : std::stoi(blockData[35]));
            block->MaxStacking                  = (blockData[36] == "" ? 40 : std::stoi(blockData[36]));
            block->SleepSuitability             = (blockData[37] == "" ? 0 : std::stof(blockData[37]));
            block->FrictionFactor               = (blockData[38] == "" ? 1.0f : std::stof(blockData[38]));
            block->Density                      = (blockData[39] == "" ? 4.0f : std::stof(blockData[39]));
            block->NoAutoJump                   = (blockData[40] == "TRUE" ? true : false);
            block->NoSmoothRise                 = (blockData[41] == "TRUE" ? true : false);
            block->FuelHeatLevel                = (blockData[42] == "" ? 0 : std::stof(blockData[42]));
            block->FuelFireDuration             = (blockData[43] == "" ? 0 : std::stof(blockData[43]));
            block->DefaultSoundMaterialName     = blockData[44];
            
            block->ShovelPower                  = (blockData[45] == "" ? 1.0f : std::stof(blockData[45]));
            block->QuarryPower                  = (blockData[46] == "" ? 1.0f : std::stof(blockData[46]));
            block->HackPower                    = (blockData[47] == "" ? 1.0f : std::stof(blockData[47]));

            block->DefaultMeleePower            = (blockData[48] == "" ? 1.0f : std::stof(blockData[48]));
            block->DefaultMeleeHitProbability   = (blockData[49] == "" ? 0.66f : std::stof(blockData[49]));
            block->DefaultProjectilePower       = (blockData[50] == "" ? 1.0f : std::stof(blockData[50]));
            block->ToolLevel                    = (blockData[51] == "" ? 0 : std::stoi(blockData[51]));
            block->PlayerLevelRequired          = (blockData[52] == "" ? 1 : std::stoi(blockData[52]));
            block->Durability                   = (blockData[53] == "" ? -1 : std::stoi(blockData[53]));
            block->IsAimable                    = (blockData[54] == "TRUE" ? true : false);
            block->IsStickable                  = (blockData[55] == "TRUE" ? true : false);
            block->AlignToVelocity              = (blockData[56] == "TRUE" ? true : false);
            block->ProjectileSpeed              = (blockData[57] == "" ? 15.0f : std::stof(blockData[57]));
            block->ProjectileDamping            = (blockData[58] == "" ? 0.8f : std::stof(blockData[58]));
            block->ProjectileTipOffset          = (blockData[59] == "" ? 0 : std::stof(blockData[59]));
            block->DisintegratesOnHit           = (blockData[60] == "TRUE" ? true : false);
            block->ProjectileStickProbability   = (blockData[61] == "" ? 0 : std::stof(blockData[61]));
            block->DefaultHeat                  = (blockData[62] == "" ? 0 : std::stof(blockData[62]));
            block->FireDuration                 = (blockData[63] == "" ? 0 : std::stof(blockData[63]));
            block->ExplosionResilience          = (blockData[64] == "" ? 0 : std::stof(blockData[64]));
            block->DefaultExplosionPressure     = (blockData[65] == "" ? 0 : std::stof(blockData[65]));
            block->DefaultExplosionIncendiary   = (blockData[66] == "TRUE" ? true : false);
            block->IsExplosionTransparent       = (blockData[67] == "TRUE" ? true : false);
            if(blockData[68] == "Shovel")
            {
                block->DigMethod = BlockDigMethod::Shovel;
            }
            else if(blockData[68] == "Quarry")
            {
                block->DigMethod = BlockDigMethod::Quarry;
            }
            else if(blockData[68] == "Hack")
            {
                block->DigMethod = BlockDigMethod::Hack;
            }
            else
            {
                block->DigMethod = BlockDigMethod::None;
            }
            block->DigResilience                = (blockData[69] == "" ? 0 : std::stof(blockData[69]));
            block->ProjectileResilience         = (blockData[70] == "" ? 0 : std::stof(blockData[70]));
            block->DefaultNutritionalValue      = (blockData[71] == "" ? 0 : std::stof(blockData[71]));
            block->DefaultSicknessProbability   = (blockData[72] == "" ? 0 : std::stof(blockData[72]));
            if(blockData[73] == "Meat")
            {
                block->FoodType = BlockFoodType::Meat;
            }
            else if(blockData[73] == "Fish")
            {
                block->FoodType = BlockFoodType::Fish;
            }
            else if(blockData[73] == "Fruit")
            {
                block->FoodType = BlockFoodType::Fruit;
            }
            else if(blockData[73] == "Grass")
            {
                block->FoodType = BlockFoodType::Grass;
            }
            else if(blockData[73] == "Bread")
            {
                block->FoodType = BlockFoodType::Bread;
            }
            else
            {
                block->FoodType = BlockFoodType::None;
            }
            block->DefaultRotPeriod             = (blockData[74] == "" ? 0 : std::stoi(blockData[74]));
            block->DefaultTextureSlot           = blockData[75];
            block->DestructionDebrisScale       = blockData[76];
        }
        else
        {
            throw std::runtime_error(fmt::format("未找到方块 {} 的数据", block->getBlockName()));
        }
    }

    /**
     * @brief 方块损坏，需要有耗损参数才有效
     * @param value 方块值，耗损次数
     * @note 若方块没有耗损的参数，那么耗损多少次都返回的都是原来的方块。
     * @return 耗损完后得新方块值,若耗损值降低到0，那么则会返回0
    */
    int32_t BlocksManager::damageItem(int32_t value, int32_t damageCount)
    {
        int32_t num = Terrain::ExtractContents(value);
        Block *block = blocks[num];
        if(block->Durability >= 0)
        {
            int32_t num2 = block->getDamage(value) + damageCount;
            if(num2 <= block->Durability)
            {
                return block->setDamage(value, num2);
            }
            return block->getDamageDestructionValue(value);
        }
        return value;
    }

    /**
     * @brief 加载方块数据源
     * @param blocksData 也就是 BlocksData.txt 的文本内容
     * @note 这里会先按行切割再按';'进行切割，然后提取名字，以名字为key，将数据内容存放到map表里面
    */
    void BlocksManager::loadBlockSource(const std::string &blocksData)
    {
        std::vector<std::string> lineData;
        Utils::StringSplit(blocksData, "\n", lineData);

        int32_t num = 0;
        for(auto it = lineData.begin(); it != lineData.end(); it++)
        {
            std::vector<std::string> tarBlockData;
            Utils::StringSplit(*it, ";", tarBlockData);
            if(tarBlockData.size() > 0 && tarBlockData[0] != "" && tarBlockData[0] != "Class Name")
            {
                if(tarBlockData.size() != 78)
                {
                    Log::Warn(fmt::format("方块 {} 的数据个数不是78个，已经跳过", tarBlockData[0]));
                    break;   
                }
                std::string blockName = tarBlockData[0];

                // Log::info(fmt::format("获取到 {} 的数据， 总共有 {} 个数据", blockName, tarBlockData.size()));
                m_blockDataMaps[blockName] = std::move(tarBlockData);
            }
            num++;
        }
        Log::Info(fmt::format("添加了 {} 行方块数据", num));
    }

} // namespace PocketSurvival
